<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html>
<head>
<title>wxPython gripts</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="wxPython, gripts, small code examples, graphical scripts, Python">
<meta name="description" content="This part of the wxPython tutorial covers creating small gui scripts in wxPython.">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>


<div class="content">


<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>wxPython Gripts</h1>

<p>
In this section we will show some small, complete scripts. These graphical 
scripts or "gripts" will demonstrate various areas in programming. 
</p>

<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* NewSquare */
google_ad_slot = "0364418177";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 

<p>
We present three gripts. The first will send an email message. The second
will connect to the anonymous ftp account and display a connected or disconnected
image in the statusbar. The final one will create a small puzzle game.
</p>


<h2>Tom</h2>

<p>
Tom is a simple script that sends an email. 
</p>

<pre class="code">
#!/usr/bin/python
# -*- coding: utf-8 -*-


import wx
import smtplib

class Example(wx.Dialog):
    
    def __init__(self, *args, **kw):
        super(Example, self).__init__(*args, **kw)
        
        self.InitUI()
        
    def InitUI(self):
        
        pnl = wx.Panel(self)
        
        vbox = wx.BoxSizer(wx.VERTICAL)
        hbox1 = wx.BoxSizer(wx.HORIZONTAL)
        hbox2 = wx.BoxSizer(wx.HORIZONTAL)
        hbox3 = wx.BoxSizer(wx.HORIZONTAL)

        st1 = wx.StaticText(pnl, label='From')
        st2 = wx.StaticText(pnl, label='To ')
        st3 = wx.StaticText(pnl, label='Subject')

        self.tc1 = wx.TextCtrl(pnl, size=(180, -1))
        self.tc2 = wx.TextCtrl(pnl, size=(180, -1))
        self.tc3 = wx.TextCtrl(pnl, size=(180, -1))

        self.tc = wx.TextCtrl(pnl, style=wx.TE_MULTILINE)
        button_send = wx.Button(pnl, label='Send')

        hbox1.Add(st1, flag=wx.LEFT, border=10)
        hbox1.Add(self.tc1, flag=wx.LEFT, border=35)
        hbox2.Add(st2, flag=wx.LEFT, border=10)
        hbox2.Add(self.tc2, flag=wx.LEFT, border=50)
        hbox3.Add(st3, flag=wx.LEFT, border=10)
        hbox3.Add(self.tc3, flag=wx.LEFT, border=20)
        vbox.Add(hbox1, flag=wx.TOP, border=10)
        vbox.Add(hbox2, flag=wx.TOP, border=10)
        vbox.Add(hbox3, flag=wx.TOP, border=10)
        vbox.Add(self.tc, proportion=1, flag=wx.EXPAND | wx.TOP | 
            wx.RIGHT | wx.LEFT, border=15)
        vbox.Add(button_send, flag=wx.ALIGN_CENTER | wx.TOP | 
            wx.BOTTOM, border=20)

        self.Bind(wx.EVT_BUTTON, self.OnSend, button_send)
        pnl.SetSizer(vbox)

        self.SetSize((400, 420))
        self.SetTitle('Tom')
        self.Centre()
        self.ShowModal()
        self.Destroy()

    def OnSend(self, e):
        
        sender = self.tc1.GetValue()
        recipient = self.tc2.GetValue()
        subject = self.tc3.GetValue()
        text = self.tc.GetValue()
        header = 'From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n' % 
            (sender, recipient, subject)
        message = header + text

        try:
            
            server = smtplib.SMTP('mail.chello.sk')
            server.sendmail(sender, recipient, message)
            server.quit()
            dlg = wx.MessageDialog(self, 'Email was successfully sent', 'Success', 
                wx.OK | wx.ICON_INFORMATION)
            dlg.ShowModal()
            dlg.Destroy()

        except smtplib.SMTPException, error:
            
            dlg = wx.MessageDialog(self, 'Failed to send email', 
                'Error', wx.OK | wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()


def main():
    
    ex = wx.App()
    Example(None)
    ex.MainLoop()    


if __name__ == '__main__':
    main()  
</pre>

<p>
We have a dialog window with from, to and subject text controls and 
a message text control. After pushing the send button the email is 
sent to the recipient. 
</p>

<pre class="explanation">
import smtplib
</pre>

<p>
For working with emails we need to import smtp module. This module 
is part of the Python language.
</p>

<pre class="explanation">
header = 'From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n' % 
    (sender, recipient, subject)
</pre>

<p>
From, To and Subject options must be separated by carriedge return and 
newline as shown here. This is requested by RFC 821 norm. So we must follow it.
</p>

<pre class="explanation">
server = smtplib.SMTP('mail.chello.sk')
server.sendmail(sender, recipient, message)
server.quit()
</pre>

<p>
Next we create an SMTP connection. Here you specify your settings. Each ISP gives you 
the name of the pop and smtp servers. In my case, 'mail.chello.sk' is a name for both. 
A mail is sent by calling the sendmail() method. Finally, we quit the connection with 
the quit() method.
</p>

<img src="/img/gui/wxpython/tom.png" alt="Tom">
<div class="figure">
Figure: Tom
</div>


<h2>Kika</h2>

<p>
Kika is a gript that connects to an ftp site. If a login is successfull, Kika shows a connected 
icon on the statusbar. Otherwise, a disconnected icon is displayed. We use an ftplib module from 
the Python standard library. If you do not have an ftp account, you can try to login to some 
anonymous ftp sites. 
</p>

<pre class="code">
#!/usr/bin/python


from ftplib import FTP, all_errors
import wx


class MyStatusBar(wx.StatusBar):
    
    def __init__(self, parent):
        super(MyStatusBar, self).__init__(parent)

        self.SetFieldsCount(2)
        self.SetStatusText('Welcome to Kika', 0)
        self.SetStatusWidths([-1, 50])
        
        self.icon = wx.StaticBitmap(self, bitmap=wx.Bitmap('disconnected.png'))
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.PlaceIcon()

    def PlaceIcon(self):
        
        rect = self.GetFieldRect(1)
        self.icon.SetPosition((rect.x+5, rect.y+1))

    def OnSize(self, e):
                
        e.Skip()
        self.PlaceIcon()
        

class Example(wx.Frame):
           
    def __init__(self, *args, **kw):
        super(Example, self).__init__(*args, **kw) 
        
        self.InitUI()
        
    def InitUI(self):    

        wx.StaticText(self, label='Ftp site', pos=(10, 20))
        wx.StaticText(self, label='Login', pos=(10, 60))
        wx.StaticText(self, label='Password', pos=(10, 100))

        self.ftpsite = wx.TextCtrl(self, pos=(110, 15), 
            size=(120, -1))
        self.login = wx.TextCtrl(self,  pos=(110, 55), 
            size=(120, -1))
        self.password = wx.TextCtrl(self, pos=(110, 95), 
            size=(120, -1), style=wx.TE_PASSWORD)

        self.ftp = None

        con = wx.Button(self, label='Connect', pos=(10, 160))
        discon = wx.Button(self, label='DisConnect', pos=(120, 160))

        self.Bind(wx.EVT_BUTTON, self.OnConnect, con)
        self.Bind(wx.EVT_BUTTON, self.OnDisConnect, discon)
        self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize) 
        self.Bind(wx.EVT_SHOW, self.OnShown)

        self.sb = MyStatusBar(self)
        self.SetStatusBar(self.sb)
        
        self.SetSize((250, 270))
        self.SetTitle('Kika')
        self.Centre()
        self.Show()
        
 
    def OnShown(self, e):
        
        if self.sb:
            self.sb.PlaceIcon() 
 
    def OnMaximize(self, e):
        
        self.sb.PlaceIcon()             

    def OnConnect(self, e):
        
        if not self.ftp:
            
            ftpsite = self.ftpsite.GetValue()
            login = self.login.GetValue()
            password = self.password.GetValue()

            try:
                self.ftp = FTP(ftpsite)
                var = self.ftp.login(login, password)
                    
                self.sb.SetStatusText('User connected')
                self.sb.icon.SetBitmap(wx.Bitmap('connected.png'))

            except AttributeError:

                self.sb.SetStatusText('Incorrect params')
                self.ftp = None

            except all_errors, err:
                
                self.sb.SetStatusText(str(err))
                self.ftp = None

    def OnDisConnect(self, e):
        
        if self.ftp:
            
            self.ftp.quit()
            self.ftp = None
            
            self.sb.SetStatusText('User disconnected')
            self.sb.icon.SetBitmap(wx.Bitmap('disconnected.png'))


def main():
    
    ex = wx.App()
    Example(None)
    ex.MainLoop()    


if __name__ == '__main__':
    main()   
</pre>

<p>
In the code example, we will connect to an ftp site and
show connected or disconnected icons in the statusbar. 
</p>

<pre class="explanation">
from ftplib import FTP, all_errors
</pre>

<p>
We use the standard ftplib Python module.
</p>

<pre class="explanation">
self.SetFieldsCount(2)
self.SetStatusText('Welcome to Kika', 0)
self.SetStatusWidths([-1, 50])
</pre>

<p>
Our custom statusbar will have two fields. A welcome message is shown
in the first field of the statusbar with the <code>SetStatusText()</code>
method. The <code>SetStatusWidths()</code> method arranges the width for the
status fields. The second field has a fixed width, the first one takes the
rest of the statusbar's width. 
</p>

<pre class="explanation">
def PlaceIcon(self):
    
    rect = self.GetFieldRect(1)
    self.icon.SetPosition((rect.x+5, rect.y+1))
</pre>

<p>
The PlaceIcon() method positions the icon on the statusbar. 
With the <code>GetFieldRect()</code> method we determine the size of the
second field of the statusbar. Later we place the icon
using the <code>SetPosition()</code> method.
</p>

<pre class="explanation">
def OnSize(self, e):
            
    e.Skip()
    self.PlaceIcon()
</pre>

<p>
Notice that each time the window is resized, we must position our 
icon to a new place. 
</p>

<pre class="explanation">
self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize) 
self.Bind(wx.EVT_SHOW, self.OnShown)
</pre>

<p>
We also react to EVT_MAXIMIZE and EVT_SHOW events. In their
event handlers, we replace the icon on the statusbar. 
</p>

<pre class="explanation">
try:
    self.ftp = FTP(ftpsite)
    var = self.ftp.login(login, password)
        
    self.sb.SetStatusText('User connected')
    self.sb.icon.SetBitmap(wx.Bitmap('connected.png'))
</pre>

<p>
We login to the provided ftp site. A connected icon is
shown in the statusbar. 
</p>

<pre class="explanation">
def OnDisConnect(self, e):
    
    if self.ftp:
        
        self.ftp.quit()
        self.ftp = None
        
        self.sb.SetStatusText('User disconnected')
        self.sb.icon.SetBitmap(wx.Bitmap('disconnected.png'))
</pre>

<p>
In the OnDisConnect() method, we quit the connection to the 
ftp site, if there is any. The statusbar will show a
disconnected icon.
</p>

<img src="/img/gui/wxpython/kika.png" alt="Kika">
<div class="figure">
Figure: Kika
</div>


<h2>Puzzle</h2>

<p>
In this gript, we introduce a puzzle game. We have an image of a Sid character 
from the Ice Age movie. The goal is to form the picture. 
</p>

<pre>
$ convert sid.png -crop 120x90 sid%d.png
$ ls
sid0.png  sid2.png  sid4.png  sid6.png  sid8.png
sid1.png  sid3.png  sid5.png  sid7.png  sid.png
</pre>

<p>
ImageMagick program can be used to easily slice an image to smaller
images. If your image is 360x270, the above command will crop the image
into nine parts. 
</p>

<pre class="code">
#!/usr/bin/python
# -*- coding: utf-8 -*-


import wx
import random

class Example(wx.Dialog):
    
    def __init__(self, *args, **kw):
        super(Example, self).__init__(*args, **kw)
        
        self.InitUI()
        
    def InitUI(self):
        
        images = ['sid1.png', 'sid2.png', 'sid3.png', 'sid4.png', 
                'sid5.png', 'sid6.png', 'sid7.png', 'sid8.png']

        self.pos = [ [0, 1, 2], [3, 4, 5], [6, 7, 8] ]

        self.sizer = wx.GridSizer(3, 3, 0, 0)

        numbers = [0, 1, 2, 3, 4, 5, 6, 7]
        random.shuffle(numbers)

        for i in numbers:
            
                btn = wx.BitmapButton(self, i, wx.Bitmap(images[i]))
                btn.Bind(wx.EVT_BUTTON, self.OnPressButton, btn)
                self.sizer.Add(btn)

        self.empty = wx.BitmapButton(self, bitmap=wx.Bitmap('empty.png'))
        self.empty.Bind(wx.EVT_BUTTON, self.OnPressButton, self.empty)
        self.sizer.Add(self.empty)

        self.SetSizerAndFit(self.sizer)
        self.SetTitle('Puzzle')
        self.Centre()
        self.ShowModal()
        self.Destroy()

    def OnPressButton(self, e):
        
        btn = e.GetEventObject()
        
        width = self.empty.GetSize().x
        height = self.empty.GetSize().y

        btnX = btn.GetPosition().x
        btnY = btn.GetPosition().y
        emptyX = self.empty.GetPosition().x
        emptyY = self.empty.GetPosition().y
    
        
        if (((btnX == emptyX) and (emptyY - btnY) == height)
          or ((btnX == emptyX) and (emptyY - btnY) == -height)
          or ((btnY == emptyY) and (emptyX - btnX) == width)
          or ((btnY == emptyY) and (emptyX - btnX) == -width)):
                 
            self.ExchangeImages(btn)

            
    def ExchangeImages(self, btn):
        
        bmp1 = self.empty.GetBitmapLabel()
        bmp2 = btn.GetBitmapLabel()
        
        self.empty.SetBitmapLabel(bmp2)
        btn.SetBitmapLabel(bmp1)
        
        self.empty = btn        


def main():
    
    ex = wx.App()
    Example(None)
    ex.MainLoop()    


if __name__ == '__main__':
    main()  
</pre>

<p>
The image is sliced into 9 subimages. 8 subimages are used in the program. One 
empty image is used as well. 
</p>

<pre class="explanation">
images = ['sid1.png', 'sid2.png', 'sid3.png', 'sid4.png', 
                'sid5.png', 'sid6.png', 'sid7.png', 'sid8.png']
</pre>

<p>
These are the images, that will be shown in button widgets. 
</p>

<pre class="explanation">
self.sizer = wx.GridSizer(3, 3, 0, 0)
</pre>

<p>
For this gript, <code>wx.GridSizer</code> fits ideally.
</p>

<pre class="explanation">
numbers = [0, 1, 2, 3, 4, 5, 6, 7]
random.shuffle(numbers)
</pre>

<p>
We have eight numbers. Those numbers are shuffled so that we have a random number order. 
Each time we start the gript, we will have a different order of bitmaps. 
</p>

<pre class="explanation">
for i in numbers:
    
        btn = wx.BitmapButton(self, i, wx.Bitmap(images[i]))
        btn.Bind(wx.EVT_BUTTON, self.OnPressButton, btn)
        self.sizer.Add(btn)
</pre>

<p>
In this for loop, we create 8 bitmap buttons with the provided images. 
To each button an event handler is attached. 
</p>

<pre class="explanation">
self.empty = wx.BitmapButton(self, bitmap=wx.Bitmap('empty.png'))
self.empty.Bind(wx.EVT_BUTTON, self.OnPressButton, self.empty)
self.sizer.Add(self.empty)
</pre>

<p>
The ninth button displays an empty image. The self.empty variable serves
as a pointer to the button, which has an empty image. Buttons will exchange
their images so the self.empty variable will point to different buttons
over time. 
</p>

<pre class="explanation">
def OnPressButton(self, e):
    
    btn = e.GetEventObject()
    
    width = self.empty.GetSize().x
    height = self.empty.GetSize().y
    ...
</pre>

<p>
When we press a button, the OnPressButton() handler is called. 
The <code>GetEventObject()</code> method retrieves the event source;
it is the button that triggerd the EVT_BUTTON event. 
We get the width and height of the emtpy button. (All the buttons
have the same size). These values will be used to determine the 
buttons adjacent to the empty button.
</p>

<pre class="explanation">
btnX = btn.GetPosition().x
btnY = btn.GetPosition().y
emptyX = self.empty.GetPosition().x
emptyY = self.empty.GetPosition().y
</pre>

<p>
We get the x, y coordinates of the currently pressed button and the
empty button.
</p>

<pre class="explanation">
if (((btnX == emptyX) and (emptyY - btnY) == height)
    or ((btnX == emptyX) and (emptyY - btnY) == -height)
    or ((btnY == emptyY) and (emptyX - btnX) == width)
    or ((btnY == emptyY) and (emptyX - btnX) == -width)):
            
    self.ExchangeImages(btn)
</pre>

<p>
Here we find out, if the pressed button is adjacent to the
empty button. If true, we call the ExchangeImages() method
which will switch the images for the two buttons. 
</p>

<pre class="explanation">
def ExchangeImages(self, btn):
    
    bmp1 = self.empty.GetBitmapLabel()
    bmp2 = btn.GetBitmapLabel()
    
    self.empty.SetBitmapLabel(bmp2)
    btn.SetBitmapLabel(bmp1)
    
    self.empty = btn    
</pre>

<p>
In the ExchangeImages() method, we get the images of the
two buttons in question. We switch them. Finally, the
self.empty button points to the new button, which has the 
empty image. 
</p>

<img src="/img/gui/wxpython/puzzle.jpg" alt="Puzzle">
<div class="figure">
Figure: Puzzle
</div>

<p>
In this chapter, we have presented three interesting gripts. 
</p>


<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="/wxpython">Contents</a></span> ‡ 
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>



<div class="footer">
<div class="signature">
<a href="/">ZetCode</a>  last modified March 25, 2012  <span class="copyright">&copy; 2007 - 2013 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>